- 正则表达式是用来进行文本处理的技术,是语言无关的,在几乎所有语言中都有实现。javascript中还会用得到。
- 一个正则表达式是由普通字符串以及特殊字符字符(称为元字符)组成的文字模式,该模式描述在查找文字主体时,待匹配的一个或多个字符串。正则表达式作为一个模板,将某个字符模式与所搜索的字符串进行比较。
- 就像通配符“*.jpg”、“%ab%”,它是对字符串进行匹配的特殊字符串。
- 正则表达式是非常复杂的,不要希望一次能掌握,理解正则表达式能做什么(字符串的匹配,字符串的提取,字符串的替换),掌握常用的正则表达式语法,以后用到再查就行。
- 找工作的亮点,后面项目中的采集器,敏感词过滤、URLRewite、Validator也会涉及到正则表达式。
- 正则表达式是对字符串进行操作的
- 理解元字符是一个必须攻克的难关
(1).:匹配除\n之外的任何一个字符。例如正则表达式“b.g”能匹配如下字符串:“big”、“bug”、“b g”,但是不匹配“buug”,"b..g"可以匹配“buug”。
(2)[]:匹配括号中的任何一个字符。例如正则表达式“b[aui]g”匹配bug、big和bag,但是不匹配beg、baug。也可以在括号中使用连字符“-”来指定字符的区间。例如正则表达式[0-9]可以匹配任何数字字符,这样正则表达式"a[0-9]c"等价于"a[0123456789]c"就可以匹配“a0c”、“a1c”,"a2c"等字符串;还可以制定多个区间,例如“[A-Za-z]”可以匹配任何大小的字母,“[A-Za-z0-9]”,可以匹配任何的大小写字母或者数字。
匹配11-20之间的数字,正则表达式为[12][0-9],不能写成[10-29],因为这里的10和29只是表示字符1 或0 2或9
(2)|:将这个匹配条件进行逻辑"或"运算。‘z|food’能匹配“z”或“food”。‘(z|f)ood’则匹配“zood”或“food”。注意^$。[ab]等价于a|b。匹配的时候将"|"左右两边分别作为一个整体
(3)():将()之间括起来的表达式定义为“组”(group),并且将匹配这个表达式的字符保存到一个临时区域,这个元字符在字符串提取的时候非常有用。把一个字符串表示为一个整体。改变优先级,定义提取组两个作用。
限定符:限定前面的表达式出现的次数。
(4)*:匹配多个在它之前的子表达式,和通配符没有关系。例如正则表达式“zo*”等同于“z(o*)”能匹配“z”,"zo"以及"zoo",因此"*“意味着能够匹配任意字符串。
”z(b|c)*“可以匹配zb,zbc,zcb,zcccc,zbbbcc。”z(ab)*"能匹配zab,zabab(用括号表示优先级)。
(5)+:匹配前面的子表达式一次或多次,和*对比(0到多次)。例如正则表达式,9+匹配9,99,999等。"zo+“以及”zoo",不能匹配"z“。
(6)?:匹配前面的子表达式零次或一次。例如”do(es)?"可以匹配"do“,”does"。一般用来匹配"可选部分“。
(7){n}:匹配确定的n次。”zo{2}“-zoo。例如”e{2}“不能匹配”bed“中的”e“,但是能匹配”seed“中的两个”e“。
(8){n,}:至少匹配n次。例如”e{2,}“不能bed中的”e“,但能匹配”seeeeeed“中的所有”e“。
(9){n,m}:最少匹配n次最多匹配m次。”e{1,3}“
(10)^匹配一行的开始,例如正则表达式”^regex“能够匹配字符串“^regex我会用”的开始,但是不能匹配“我会用regex”。^另外一种意思:非[^0-9],放在中括号里面表示非,其余地方都表示为匹配一行的开始
(11)$匹配行结束符。例如正则表达式“浮云$”,能够匹配字符串“一切都是浮云”的末尾,但是不能匹配字符串“浮云呀”。
- 简写表达式
♦注意简写表达式是不考虑转义符的。这里的\就表示字符\,而不是c#级别的\,在c#代码中使用@或\双重转义。区分c#级别的转义和正则表达式级别的转义,恰好c#的转义\和正则表达式级别的转义符都是\而已。正则表达式的转义是在c#之后的(层层盘剥)。把c#的转义想成%就明白了@“\."就是"\.“这个普通的字符串,只不过在正则表达式来看,它有了特殊的含义@”\d“或者”\\d“。
-》\d:代表一个数字,等同于[0-9]
-》\D:代表非数字,等同于[^0-9]
-》\s:代表换行符、Tab制表符等空白字符(一般我们用[\s\S]来匹配任意字符串)
-》\S:代表非空白字符
-》\w:匹配字母或数字或下划线或汉字,即能组成单词的字符。
-》\W:非\w,等同于[^\W]
d:digital;s:space;w:word。大写就是”非“
- 简单正则表达式的练习
1.匹配正确的邮政编码 ^\d{6}$
2.判断是是不是身份证号码(15位数字或18位数字后面可能有X)^(\d{15}|\d{18}[xX]?)$ 或者^(\d{15}|\d{17}[dxX])$
3.匹配国内电话号
-》三位或四位区号-八位或七位电话号码 ^((\d{3}|\d{4})-(\d{8}|\d{7}))$
-》11位数字的手机号码 ^\d{11}$(不严格)
-》服务电话10086、10010、95599、95588等 ^((100|955)\d{2})$(不严格)
- .Net的正则表达式
♦正则表达式在.Net中就是用字符串表示,这个字符串格式比较特殊,无论多么特殊,在c#语言看来都是普通的字符串,具体什么含义由Regex类内部进行分析。
♦如何匹配大于10小于20的字符串^[1][1-9]$(正则表达式是对字符串的操作)
♦正则表达式(Regular Expression)的主要类:Regex
♦常用的四种情况:(C#语法)
-》判断是否匹配Regex.IsMatch(”字符串","正则表达式“)
-》字符串提取:Regex.Match(”字符串","要提取的字符串的正则表达式")//只能提取一个(提取一次)
-》字符串提取(循环提取所有)Regex.Matchs()//可以提取所有匹配的字符串
-》字符串替换Regex.Replace("字符串",“正则”,“替换内容”)
string str = "fjdjfdfd fj123jdkfjdjfdjf45dfjkdjfkdj1233"; Match m=Regex.Match(str,@"\d+");//返回第一个匹配的字符串 if (m.Success) { Console.WriteLine("匹配到的字符串为{0}",m.Value); Console.WriteLine("匹配的字符串的起始位置是{0}",m.Index); Console.WriteLine("匹配到的字符串长度为{0}", m.Length); } MatchCollection mc=Regex.Matches(str,@"\d+");//匹配所有的字符串 Console.WriteLine("=================="); //用for循环遍历 for (int i = 0; i < mc.Count; i++) { Match m1 = mc[i]; Console.WriteLine("匹配到的字符串为{0}", m1.Value); Console.WriteLine("匹配的字符串的位置是{0}", m1.Index); Console.WriteLine("匹配到的字符串长度为{0}", m1.Length); Console.WriteLine(); } Console.WriteLine("==================="); //foreach遍历 foreach(Match item in mc){ Console.WriteLine(item.ToString()); } Console.ReadKey();
- 难点:利用圆括号对匹配的结果进行分组
(案例,提取文本文件里特定格式的字符串)
//读取html文本
// | //// | //......... 里的图片路径 string html = System.IO.File.ReadAllText("美女们.htm",Encoding.Default); string regex = "src=\"(.+)\"";//正则表达式中圆括号有分组的功能,从左往右数圆括号,一次为Groups[1]、Groups[2]、Groups[3]...... MatchCollection mc=Regex.Matches(html, regex); foreach (Match m in mc) { Console.WriteLine(m.Groups[1]); } Console.ReadKey(); |
再如例子
string str = "012345"; // 12 3 4 5 string regex = @"((\d(\d))(\d))(\d)"; Match m = Regex.Match(str, regex); Console.WriteLine(m.Value);//结果为0123 Console.WriteLine("第1组为:"+m.Groups[1]);//结果为0123 Console.WriteLine("第2组为:" + m.Groups[2]);//结果为012 Console.WriteLine("第3组为:" + m.Groups[3]);//结果为01 Console.WriteLine("第4组为:" + m.Groups[4]);//结果1 Console.WriteLine("第5组为:" + m.Groups[5]);//结果为0 Console.ReadKey();
上述代码中,利用了正则表达式圆括号分组功能,直接将匹配到的字符串进行分组以提取我们自己所需要的内容。
所以,我们可以尝试用正则匹配一个文件的全路径的文件名和后缀名
string file = @"C:\123\4565\789\abcd.mp3"; string regex = @"\\([^\\]+)(\.[^\\]+)$"; MatchCollection m = Regex.Matches(file, regex); foreach (Match item in m) { Console.WriteLine(item.Value);//匹配结果为abcd.mp3 Console.WriteLine(item.Groups[1].Value);//匹配结果为abcd Console.WriteLine(item.Groups[2].Value);//匹配结果为.mp3 } Console.ReadKey();
易错题目:
string s=@"((\d)(\w))+"; string r="1a2b3c"; Match m = Regex.Match(r,s); MatchCollection mc =Regex.Matches(r,s); Console.WriteLine(m.Groups[0]);//结果为1a2b3c Console.WriteLine(m.Groups[1]);//结果为3c,因为第一组(即第一个括号)是匹配一个数字和一个字符。本来最先匹配到的是1a,但后面陆续被2b,3c覆盖了所以最后结果为3c。好比一个变量第一次存储1a,第二次改为存储2b,第三次为3c Console.WriteLine(m.Groups[2]);//结果为3 Console.WriteLine(m.Groups[3]);//结果为c Console.ReadKey(); }
若上述易错题中,将((\d)(\w)+),那么匹配的第一组就是1a2b3c,因为第一组(第一个括号内)匹配的表达式为为一个或者多个\d\w
- 匹配IP地址,4段用.分割的最多三位数字。192.168.54.77,假设333.3333.3.33也是正确的 ^(\d{1,3}(\.\d{1,3}){3})$
说明:以上匹配IP地址的正则表达式其实严格意义上说是不正确的。IP地址的每个节点其实都是0-255的数字
严谨的IP:^(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9])\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])$
6.匹配Url(比如、ftp://127.0.0.1/1.txt)
说明:url是uniform resource locator
组成://地址/目录?参数1=参数值&参数1=参数值....
简单解释:协议http(hyper text translate protocal)传输协议 网站 ;协议 ftp(file translate protocal ) 下载;
端口:入口
目录:定位一台计算机下的目录文件下
匹配url的正则表达式:^\W+://.+$
string regex = @"^(https?|ftp)://([^:]+?)(:\d+)?(/[^\?]+?(\?\w+=\w+(&\w+=\w+)*)?)?$"; // regex = @"(.+?)://(.+?)(:\d+)?(/.+?)\?(.+)";//也可以 string url1 = @"http://www.123.com:8081/1.html?k=1&y=2"; string url2 = @"http://www.123.com/yangzhongke?wd=1"; Console.WriteLine(Regex.IsMatch(url1,regex));//结果为true Console.WriteLine(Regex.IsMatch(url2,regex));//结果为true Console.ReadKey();
- 正则表达式的贪婪模式(难点)
//贪婪模式:从左往右递减,正则表达式只有两个权级,一个是尽可能匹配,一个尽可能不匹配 string str="12345"; string regex=@"(\d+)(\d+)(\d+)"; Match mc=Regex.Match(str,regex); Console.WriteLine(@"第一个\d+匹配到的为:"+mc.Groups[1]);//结果匹配为123 Console.WriteLine(@"第二个\d+匹配到的为:"+mc.Groups[2]);//结果匹配为4 Console.WriteLine(@"第三个\d+匹配到的为:"+mc.Groups[3]);//结果匹配为5 Console.WriteLine("=========================="); string regex1 = @"(\d*?)(\d*)(\d+)";//问号表示取消贪婪模式 Match mc1 = Regex.Match(str, regex1); Console.WriteLine(@"第一个\d+匹配到的为:" + mc1.Groups[1]);//结果为不匹配 Console.WriteLine(@"第二个\d+匹配到的为:" + mc1.Groups[2]);//结果匹配为1234 Console.WriteLine(@"第三个\d+匹配到的为:" + mc1.Groups[3]);//结果匹配为5 Console.ReadKey();
那么,接下来我们可以尝试用正则表达式来实现Path类的几个静态方法:GetDirectoryName()、GetFileName()、GetExtension()、GetFileNameWitoutExtension()
string file = @"C:\123\4565\789\abcd.mp3"; string regex = @"((.:)\\.+\\)((.+)(\..+))"; Match mc = Regex.Match(file, regex); Console.WriteLine("匹配的原始数据为:" + mc.Value); Console.WriteLine("文件所在的磁盘为:" + mc.Groups[2]); Console.WriteLine("文件的全路径为:" + mc.Groups[1]); Console.WriteLine("文件的文件名为:" + mc.Groups[3]); Console.WriteLine("文件的后缀名为:" + mc.Groups[5]); Console.WriteLine("文件的去后缀名的文件名为:" + mc.Groups[4]); // @"((.:)\\.+\\)((.+)(\..+))"; Console.WriteLine("==============="); string file1= @"C:\abcd.mp3"; regex = @"((.:)\\(.+\\)?)((.+)(\..+))"; Match mc1 = Regex.Match(file1,regex); Console.WriteLine("匹配的原始数据为:" + mc1.Value); Console.WriteLine("文件所在的磁盘为:" + mc1.Groups[2]); Console.WriteLine("文件的全路径为:" + mc1.Groups[1]); Console.WriteLine("文件的文件名为:" + mc1.Groups[4]); Console.WriteLine("文件的后缀名为:" + mc1.Groups[6]); Console.WriteLine("文件的去后缀名的文件名为:" + mc1.Groups[5]); Console.ReadKey();
- 案例讲解
1.练习从网站上下载图片
搭建一个服务器(运行CassiniDev.exe),将我们计算机硬盘里存储的html文件的物理路径复制到CasiniDev PysicalPath下面的文本框内,点击start,即可以生成一个可访问的网址
步骤:
(1)运行CassiniDev.exe
(2)将default.htm所在的物理路径放入CassiniDev 地下的PysicalPath下的文本框内
(3)点击start
代码:
WebClient wc =new WebClient();//用来从网站上下载资源的类 string html = wc.DownloadString("http://localhost:8080/default.htm"); string r= @""; // r = @"
2.从网页上提取Email
WebClient wc = new WebClient(); wc.Encoding = Encoding.UTF8;//因为该html文件的charset=UTF-8; string htmlText = wc.DownloadString("http://localhost:8080/大家留下email交友吧_email_天涯社区.htm"); string regex = @"[\da-zA-Z\.\-_]+@\w+\.[a-zA-Z]+"; MatchCollection mc = Regex.Matches(htmlText, regex); foreach (Match item in mc) { Console.WriteLine(item.Value); //System.IO.File.WriteAllText(@"E:\上课\第12天\课堂代码\下载Email\bin\Debug\结果.txt",item.Value); } Console.ReadKey();
3.从招聘网页上提取岗位名称
WebClient wc = new WebClient(); string htmltxt = wc.DownloadString("http://localhost:8080/【上海,IT-管理,计算机软件招聘,求职】-前程无忧.htm"); string regex = @"(.+)"; MatchCollection mc = Regex.Matches(htmltxt,regex); foreach( Match item in mc) { Console.WriteLine(item.Groups[1]); } Console.ReadKey();
- 正则表达式替换的简单说明
说明:Regex.Replace(string 需要操作的字符串,string 需要替换的字符串,string替换为的字符串)跟string <string>.Replace()相似。正则表达式相对功能更强大
代码:
//将 "2013年8月9日"替换为“2013-8-9” //解法一 string str = "2013年8月9日"; str = Regex.Replace(str, "年|月", "-"); str = Regex.Replace(str, "日", ""); Console.WriteLine(str); //解法二 string str1 = "2013年9月9日"; //正则表达式中,凡是组里面的数组,都可以用"$组号"进行引用 str1 = Regex.Replace(str1,@"(\d+)年(\d+)月(\d+)日","$1-$2-$3"); Console.WriteLine(str1); //“$组号”引用匹配组的再举例 string str2=Regex.Replace("123",@"\d","$0$0"); Console.WriteLine(str2); Console.ReadKey();
- 匹配替换案例:
1.“压缩”文件(简单压缩)
//要缩文件,将js文本中的空格及注释都去掉。 string txt = System.IO.File.ReadAllText(@"E:\上课\第12天\课堂代码\正则表达式的替换\bin\Debug\jkuery_20131031.js"); txt = Regex.Replace(txt, @"//.+", ""); txt = Regex.Replace(txt,@"\s+",""); System.IO.File.WriteAllText(@"E:\上课\第12天\课堂代码\正则表达式的替换\bin\Debug\结果.js",txt); //我们还可以分析词法,将所有变量名都替换掉
2.将网址文本改为链接网址
string text = System.IO.File.ReadAllText("网址大全.txt", Encoding.Default); string regex1 = @".+"; text = Regex.Replace(text, regex1, "$0");//直接加链接 System.IO.File.WriteAllText(@"E:\上课\第12天\课堂代码\课堂练习\bin\Debug\结果.txt1", text); string regex = @"(https://)?(.+)"; text=Regex.Replace(text,regex,"$2");//让所有链接前都有一个http:// System.IO.File.WriteAllText(@"E:\上课\第12天\课堂代码\课堂练习\bin\Debug\结果.txt", text);
3.给文件名加后一个缀
Console.WriteLine("请输入一个文件名"); string str = Console.ReadLine(); str=Regex.Replace(str,@"(.+)\.(.+)","$1_mim.$2"); Console.WriteLine(str); Console.ReadKey();